home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / wais / waisgate / HTGopher.c < prev    next >
C/C++ Source or Header  |  1995-05-09  |  12KB  |  476 lines

  1. /*            GOPHER ACCESS                HTGopher.c
  2. **            =============
  3. **
  4. ** History:
  5. **    26 Sep 90    Adapted from other accesses (News, HTTP) TBL
  6. **    29 Nov 91    Downgraded to C, for portable implementation.
  7. */
  8.  
  9. /* Implements:
  10. */
  11. #include "HTGopher.h"
  12.  
  13. #define CR        FROMASCII('\015')    /* Carriage return */
  14. #define LF         FROMASCII('\012')    /* ASCII line feed
  15.                    (sometimes \n is CR on Mac) */
  16.  
  17. #define GOPHER_PORT 70        /* See protocol spec */
  18. #define BIG 1024        /* Bug */
  19. #define LINE_LENGTH 256        /* Bug */
  20.  
  21. /*    Gopher entity types:
  22. */
  23. #define GOPHER_TEXT        '0'
  24. #define GOPHER_MENU        '1'
  25. #define GOPHER_CSO        '2'
  26. #define GOPHER_ERROR        '3'
  27. #define GOPHER_MACBINHEX    '4'
  28. #define GOPHER_PCBINHEX        '5'
  29. #define GOPHER_UUENCODED    '6'
  30. #define GOPHER_INDEX        '7'
  31. #define GOPHER_TELNET        '8'
  32. #define GOPHER_GIF              'g'
  33. #define GOPHER_HTML        'h'        /* HTML */
  34. #define GOPHER_IMAGE            'I'
  35. #define GOPHER_DUPLICATE    '+'
  36. #define GOPHER_WWW        'w'        /* W3 address */
  37.  
  38. #include <ctype.h>
  39. #include "HTUtils.h"        /* Coding convention macros */
  40. #include "tcp.h"
  41.  
  42.  
  43. #include "HTParse.h"
  44. #include "HTFormat.h"
  45. #include "HTTCP.h"
  46.  
  47. /*        Hypertext object building machinery
  48. */
  49. #include "HTML.h"
  50.  
  51. #define PUTC(c) (*targetClass.put_character)(target, c)
  52. #define PUTS(s) (*targetClass.put_string)(target, s)
  53. #define START(e) (*targetClass.start_element)(target, e, 0, 0)
  54. #define END(e) (*targetClass.end_element)(target, e)
  55. #define END_TARGET (*targetClass.end_document)(target)
  56. #define FREE_TARGET (*targetClass.free)(target)
  57. struct _HTStructured {
  58.     CONST HTStructuredClass *    isa;
  59.     /* ... */
  60. };
  61.  
  62. PRIVATE HTStructured *target;            /* the new hypertext */
  63. PRIVATE HTStructuredClass targetClass;        /* Its action routines */
  64.  
  65.  
  66. #ifdef NeXTStep
  67. #include <appkit/defaults.h>
  68. #define GOPHER_PROGRESS(foo)
  69. #else
  70. #define GOPHER_PROGRESS(foo) fprintf(stderr, "%s\n", (foo))
  71. #endif
  72.  
  73. #define NEXT_CHAR HTGetChararcter()
  74.  
  75.  
  76.  
  77. /*    Module-wide variables
  78. */
  79. PRIVATE int s;                    /* Socket for GopherHost */
  80.  
  81.  
  82.  
  83. /*    Matrix of allowed characters in filenames
  84. **    -----------------------------------------
  85. */
  86.  
  87. PRIVATE BOOL acceptable[256];
  88. PRIVATE BOOL acceptable_inited = NO;
  89.  
  90. PRIVATE void init_acceptable NOARGS
  91. {
  92.     unsigned int i;
  93.     char * good = 
  94.       "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./-_$";
  95.     for(i=0; i<256; i++) acceptable[i] = NO;
  96.     for(;*good; good++) acceptable[(unsigned int)*good] = YES;
  97.     acceptable_inited = YES;
  98. }
  99.  
  100. PRIVATE CONST char hex[17] = "0123456789abcdef";
  101.  
  102. /*    Decdoe one hex character
  103. */
  104.  
  105. PRIVATE char from_hex ARGS1(char, c)
  106. {
  107.     return           (c>='0')&&(c<='9') ? c-'0'
  108.             : (c>='A')&&(c<='F') ? c-'A'+10
  109.             : (c>='a')&&(c<='f') ? c-'a'+10
  110.             :               0;
  111. }
  112.  
  113.  
  114.  
  115. /*    Paste in an Anchor
  116. **    ------------------
  117. **
  118. **    The title of the destination is set, as there is no way
  119. **    of knowing what the title is when we arrive.
  120. **
  121. ** On entry,
  122. **    HT     is in append mode.
  123. **    text     points to the text to be put into the file, 0 terminated.
  124. **    addr    points to the hypertext refernce address 0 terminated.
  125. */
  126. PRIVATE void write_anchor ARGS2(CONST char *,text, CONST char *,addr)
  127. {
  128.  
  129.  
  130.     
  131.     BOOL present[HTML_A_ATTRIBUTES];
  132.     char * value[HTML_A_ATTRIBUTES];
  133.     
  134.     int i;
  135.     
  136.     for (i=0; i<HTML_A_ATTRIBUTES; i++) present[i]=0;
  137.     present[HTML_A_HREF] = YES;
  138.     value[HTML_A_HREF] = addr;
  139.     present[HTML_A_TITLE] = YES;
  140.     value[HTML_A_TITLE] = text;
  141.     
  142.     (*targetClass.start_element)(target, HTML_A, present, value);
  143.         
  144.     PUTS(text);
  145.     END(HTML_A);
  146. }
  147.  
  148.  
  149. /*    Parse a Gopher Menu document
  150. **    ============================
  151. **
  152. */
  153.  
  154. PRIVATE void parse_menu ARGS2 (
  155.     CONST char *,        arg,
  156.     HTParentAnchor *,    anAnchor)
  157. {
  158.     char gtype;
  159.     char ch;
  160.     char line[BIG];
  161.     char address[BIG];
  162.     char *name, *selector;        /* Gopher menu fields */
  163.     char *host;
  164.     char *port;
  165.     char *p = line;
  166.     CONST char *title;
  167.  
  168. #define TAB         '\t'
  169. #define HEX_ESCAPE     '%'
  170.  
  171.     
  172.     title = HTAnchor_title(anAnchor);
  173.     if (title) {
  174.         START(HTML_H1);
  175.     PUTS(title);
  176.     END(HTML_H1);
  177.     } else
  178.         PUTS("Select one of:\n\n");
  179.     
  180.     START(HTML_MENU);
  181.     while ((ch=NEXT_CHAR) != (char)EOF) {
  182.         if (ch != LF) {
  183.         *p = ch;        /* Put character in line */
  184.         if (p< &line[BIG-1]) p++;
  185.         
  186.     } else {
  187.         *p++ = 0;        /* Terminate line */
  188.         p = line;        /* Scan it to parse it */
  189.         port = 0;        /* Flag "not parsed" */
  190.         if (TRACE) fprintf(stderr, "HTGopher: Menu item: %s\n", line);
  191.         gtype = *p++;
  192.         
  193.         /* Break on line with a dot by itself */
  194.         if ((gtype=='.') && ((*p=='\r') || (*p==0))) break;
  195.  
  196.         if (gtype && *p) {
  197.             name = p;
  198.         selector = strchr(name, TAB);
  199.         START(HTML_LI);
  200.         if (selector) {
  201.             *selector++ = 0;    /* Terminate name */
  202.             host = strchr(selector, TAB);
  203.             if (host) {
  204.             *host++ = 0;    /* Terminate selector */
  205.             port = strchr(host, TAB);
  206.             if (port) {
  207.                 char *junk;
  208.                 port[0] = ':';    /* delimit host a la W3 */
  209.                 junk = strchr(port, TAB);
  210.                 if (junk) *junk++ = 0;    /* Chop port */
  211.                 if ((port[1]=='0') && (!port[2]))
  212.                     port[0] = 0;    /* 0 means none */
  213.             } /* no port */
  214.             } /* host ok */
  215.         } /* selector ok */
  216.         } /* gtype and name ok */
  217.         
  218.         if (gtype == GOPHER_WWW) {    /* Gopher pointer to W3 */
  219.         write_anchor(name, selector);
  220.  
  221.         } else if (port) {        /* Other types need port */
  222.         if (gtype == GOPHER_TELNET) {
  223.             if (*selector) sprintf(address, "telnet://%s@%s/",
  224.                 selector, host);
  225.             else sprintf(address, "telnet://%s/", host);
  226.             
  227.         } else {            /* If parsed ok */
  228.             char *q;
  229.             char *p;
  230.             sprintf(address, "//%s/%c", host, gtype);
  231.             q = address+ strlen(address);
  232.             for(p=selector; *p; p++) {    /* Encode selector string */
  233.             if (acceptable[*p]) *q++ = *p;
  234.             else {
  235.                 *q++ = HEX_ESCAPE;    /* Means hex coming */
  236.                 *q++ = hex[(TOASCII(*p)) >> 4];
  237.                 *q++ = hex[(TOASCII(*p)) & 15];
  238.             }
  239.             }
  240.             *q++ = 0;            /* terminate address */
  241.         }
  242.         PUTS("        "); /* Prettier JW/TBL */
  243.         write_anchor(name, address);
  244.  
  245.         } else { /* parse error */
  246.             if (TRACE) fprintf(stderr,
  247.             "HTGopher: Bad menu item.\n");
  248.         PUTS(line);
  249.  
  250.         } /* parse error */
  251.         
  252.         p = line;    /* Start again at beginning of line */
  253.         
  254.     } /* if end of line */
  255.     
  256.     } /* Loop over characters */
  257.     
  258.     END(HTML_MENU);
  259.     END_TARGET;
  260.     FREE_TARGET;
  261.     
  262.     return;
  263. }
  264.  
  265. /*    Display a Gopher Index document
  266. **    -------------------------------
  267. */
  268.  
  269. PRIVATE void display_index ARGS2 (
  270.     CONST char *,    arg,
  271.     HTParentAnchor *,anAnchor)
  272. {
  273.     
  274.     START(HTML_H1);
  275.     PUTS(arg);
  276.     END(HTML_H1);
  277.     
  278.     PUTS("\nPlease enter words to search for.\n");
  279.     
  280.     if (!HTAnchor_title(anAnchor))
  281.         HTAnchor_setTitle(anAnchor, arg);
  282.     
  283.     END_TARGET;
  284.     FREE_TARGET;
  285.     return;
  286. }
  287.  
  288.  
  289. /*        De-escape a selector into a command
  290. **        -----------------------------------
  291. **
  292. **    The % hex escapes are converted. Otheriwse, the string is copied.
  293. */
  294. PRIVATE void de_escape ARGS2(char *, command, CONST char *, selector)
  295. {
  296.     CONST char * p = selector;
  297.     char * q = command;
  298.     if (command == NULL) outofmem(__FILE__, "HTLoadGopher");
  299.     while (*p) {        /* Decode hex */
  300.     if (*p == HEX_ESCAPE) {
  301.         char c;
  302.         unsigned int b;
  303.         p++;
  304.         c = *p++;
  305.         b =   from_hex(c);
  306.         c = *p++;
  307.         if (!c) break;    /* Odd number of chars! */
  308.         *q++ = FROMASCII((b<<4) + from_hex(c));
  309.     } else {
  310.         *q++ = *p++;    /* Record */
  311.     }
  312.     }
  313.     *q++ = 0;    /* Terminate command */
  314.  
  315. }
  316.  
  317.  
  318. /*        Load by name                    HTLoadGopher
  319. **        ============
  320. **
  321. **     Bug:    No decoding of strange data types as yet.
  322. **
  323. */
  324. PUBLIC int HTLoadGopher ARGS4(
  325.     CONST char *,        arg,
  326.     HTParentAnchor *,    anAnchor,
  327.     HTFormat,        format_out,
  328.     HTStream*,        sink)
  329. {
  330.     char *command;            /* The whole command */
  331.     int status;                /* tcp return */
  332.     char gtype;                /* Gopher Node type */
  333.     char * selector;            /* Selector string */
  334.  
  335.     struct sockaddr_in soc_address;    /* Binary network address */
  336.     struct sockaddr_in* sin = &soc_address;
  337.     
  338.     if (!acceptable_inited) init_acceptable();
  339.     
  340.     if (!arg) return -3;        /* Bad if no name sepcified    */
  341.     if (!*arg) return -2;        /* Bad if name had zero length    */
  342.     
  343.     if (TRACE) fprintf(stderr, "HTGopher: Looking for %s\n", arg);
  344.     
  345.     
  346. /*  Set up defaults:
  347. */
  348.     sin->sin_family = AF_INET;                /* Family, host order  */
  349.     sin->sin_port = htons(GOPHER_PORT);            /* Default: new port,  */
  350.  
  351.     if (TRACE) fprintf(stderr, "HTTPAccess: Looking for %s\n", arg);
  352.  
  353. /* Get node name and optional port number:
  354. */
  355.     {
  356.     char *p1 = HTParse(arg, "", PARSE_HOST);
  357.     int status = HTParseInet(sin, p1);
  358.         free(p1);
  359.         if (status) return status;   /* Bad */
  360.     }
  361.     
  362. /* Get entity type, and selector string.
  363. */        
  364.     {
  365.     char * p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION);
  366.         gtype = '1';        /* Default = menu */
  367.     selector = p1;
  368.     if ((*selector++=='/') && (*selector)) {    /* Skip first slash */
  369.         gtype = *selector++;            /* Pick up gtype */
  370.     }
  371.     if (gtype == GOPHER_INDEX) {
  372.         char * query;
  373.             HTAnchor_setIndex(anAnchor);    /* Search is allowed */
  374.         query = strchr(selector, '?');    /* Look for search string */
  375.         if (!query || !query[1]) {        /* No search required */
  376.         target = HTML_new(anAnchor, format_out, sink);
  377.         targetClass = *target->isa;
  378.         display_index(arg, anAnchor);    /* Display "cover page" */
  379.         return 1;            /* Local Function only */
  380.         }
  381.         *query++ = 0;            /* Skip '?'     */
  382.         command = malloc(strlen(selector)+ 1 + strlen(query)+ 2 + 1);
  383.               if (command == NULL) outofmem(__FILE__, "HTLoadGopher");
  384.           
  385.         de_escape(command, selector);    /* Bug fix TBL 921208 */
  386.  
  387.         strcat(command, "\t");
  388.       
  389.         {                    /* Remove plus signs 921006 */
  390.             char *p;
  391.         for (p=query; *p; p++) {
  392.             if (*p == '+') *p = ' ';
  393.         }
  394.         }
  395.         strcat(command, query);
  396.         
  397.     } else {                /* Not index */
  398.         command = command = malloc(strlen(selector)+2+1);
  399.         de_escape(command, selector);
  400.     }
  401.     free(p1);
  402.     }
  403.     
  404.     {
  405.     char * p = command + strlen(command);
  406.     *p++ = CR;        /* Macros to be correct on Mac */
  407.     *p++ = LF;
  408.     *p++ = 0;
  409.     /* strcat(command, "\r\n");    */    /* CR LF, as in rfc 977 */
  410.     }
  411.  
  412. /*    Set up a socket to the server for the data:
  413. */      
  414.     s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  415.     status = connect(s, (struct sockaddr*)&soc_address, sizeof(soc_address));
  416.     if (status<0){
  417.     if (TRACE) fprintf(stderr, "HTTPAccess: Unable to connect to remote host for `%s'.\n",
  418.         arg);
  419.     free(command);
  420.     return HTInetStatus("connect");
  421.     }
  422.     
  423.     HTInitInput(s);        /* Set up input buffering */
  424.     
  425.     if (TRACE) fprintf(stderr, "HTGopher: Connected, writing command `%s' to socket %d\n", command, s);
  426.     
  427. #ifdef NOT_ASCII
  428.     {
  429.         char * p;
  430.     for(p = command; *p; p++) {
  431.         *p = TOASCII(*p);
  432.     }
  433.     }
  434. #endif
  435.  
  436.     status = NETWRITE(s, command, (int)strlen(command));
  437.     free(command);
  438.     if (status<0){
  439.     if (TRACE) fprintf(stderr, "HTGopher: Unable to send command.\n");
  440.         return HTInetStatus("send");
  441.     }
  442.  
  443. /*    Now read the data from the socket:
  444. */    
  445.     switch (gtype) {
  446.     
  447.     case GOPHER_HTML :
  448.         HTParseSocket(WWW_HTML, format_out, anAnchor, s, sink);
  449.     break;
  450.  
  451.     case GOPHER_GIF:
  452.     case GOPHER_IMAGE:
  453.         HTParseSocket(HTAtom_for("image/gif"), 
  454.                format_out, anAnchor, s, sink);
  455.       break;
  456.     case GOPHER_MENU :
  457.     case GOPHER_INDEX :
  458.     target = HTML_new(anAnchor, format_out, sink);
  459.     targetClass = *target->isa;
  460.         parse_menu(arg, anAnchor);
  461.     break;
  462.             
  463.     case GOPHER_TEXT :
  464.     default:            /* @@ parse as plain text */
  465.          HTParseSocket(WWW_PLAINTEXT, format_out, anAnchor, s, sink);
  466.     break;
  467.     
  468.     } /* switch(gtype) */
  469.  
  470.     NETCLOSE(s);
  471.     return HT_LOADED;
  472. }
  473.  
  474. PUBLIC HTProtocol HTGopher = { "gopher", HTLoadGopher, NULL };
  475.  
  476.